/**
 * Created with CLion.
 * Description: 
 * User: loong
 * Date: 2023/12/19
 * Time: 20:56
 */

#include "Node.h"
#include "NodeImpl.h"

#include <QtCore>
#include <sstream>

namespace Config {

    /*class Node::NodeImpl
    {
    public:
        NodeImpl(std::string _tag, std::string _value);
        NodeImpl();
        ~NodeImpl() = default;

        static xml_node ToXmlNode(NodeSPtr _node);
        static NodeSPtr ToNode(xml_node _node);

    public:
//        xml_node ToDocument();

    public:
        xml_document doc_;

        std::string tag_name_;
        std::string value_;

        NodeWPtr                    parent_;
        QMap<std::string, NodeList> children_map_;
        NodeList                    children_list_;
        NodeAttrs                   attribute_;
    };*/

    Node::Node(std::string _key, std::string _value)
        : node_impl_(std::make_shared<NodeImpl>(_key, _value))
    {
    }

    Node::Node()
        : node_impl_(std::make_shared<NodeImpl>())
    {
    }

    bool Node::ToFile(const std::string &_file, const std::string &_indent)
    {
        return node_impl_->doc_.save_file(_file.c_str(), _indent.c_str());
    }

    std::string Node::ToString(const std::string &_indent)
    {
        auto doc = NodeImpl::ToXmlNode(shared_from_this());

        std::stringstream os;
        doc.print(os, _indent.c_str());

        return os.str();
    }

    NodeList Node::Children()
    {
        return node_impl_->children_list_;
    }

    NodeSPtr Node::AddChildren(const NodeList &_children)
    {
        for(auto child : _children) {
            if(nullptr == child)
                continue;

            if(nullptr != child->node_impl_->parent_.lock())
                child->node_impl_->parent_.lock()->RemoveChildren({child});

            this->node_impl_->children_list_.emplace_back(child);
            child->node_impl_->parent_ = shared_from_this();

            //            if(!this->node_impl_->children_map_.contains(child->node_impl_->tag_name_)) {
            //                this->node_impl_->children_map_.insert(child->node_impl_->tag_name_, child);
            //                this->node_impl_->children_list_.emplace_back(child);
            //
            //                child->node_impl_->parent_ = shared_from_this();
            //            }
        }

        return shared_from_this();
    }

    NodeSPtr Node::RemoveChildren(const NodeList &_children)
    {
        if(_children.empty()) {
            this->node_impl_->children_list_.clear();
            this->node_impl_->children_map_.clear();
        }

        for(auto child : _children) {
            if(nullptr == child)
                continue;

            this->node_impl_->children_list_.remove(child);

            auto childs = this->node_impl_->children_map_.value(child->node_impl_->tag_name_);
            std::remove_if(childs.begin(), childs.end(), [=](NodeSPtr _node) {
                return _node == child;
            });
            child->node_impl_->parent_.reset();
        }

        return shared_from_this();
    }

    NodeList Node::Child(const std::string &_name)
    {
        return this->node_impl_->children_map_.value(_name);
    }

    NodeSPtr Node::RemoveChild(const std::string &_name)
    {
        auto childs = Child(_name);
        if(childs.empty())
            return shared_from_this();

        return RemoveChildren(childs);
    }

    NodeAttrs Node::Attrs()
    {
        return this->node_impl_->attribute_;
    }

    std::string Node::Attr(const std::string &_attr_name)
    {
        return QMap(this->node_impl_->attribute_).value(_attr_name, "");
    }

    NodeSPtr Node::AddAttr(const std::string &_attr, const std::string &_value)
    {
        this->node_impl_->attribute_.insert({_attr, _value});

        return shared_from_this();
    }

    NodeSPtr Node::AddAttr(const NodeAttrs &_attr)
    {
        this->node_impl_->attribute_.insert(_attr.begin(), _attr.end());

        return shared_from_this();
    }

    NodeSPtr Node::RemoveAttr(const std::string &_attr)
    {
        this->node_impl_->attribute_.erase(_attr);

        return shared_from_this();
    }

    NodeSPtr Node::SetTagName(const std::string &_tag)
    {
        this->node_impl_->tag_name_ = _tag;

        return shared_from_this();
    }

    std::string Node::TagName()
    {
        return this->node_impl_->tag_name_;
    }

    NodeSPtr Node::SetValue(const std::string &_value)
    {
        this->node_impl_->value_ = _value;

        return shared_from_this();
    }

    std::string Node::Value()
    {
        return this->node_impl_->value_;
    }
}// namespace Config